Passed
Push — master ( ccc404...1ae359 )
by EMP
01:36
created

main.js ➔ addMsg   B

Complexity

Conditions 7

Size

Total Lines 41
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 31
dl 0
loc 41
rs 7.736
c 0
b 0
f 0
1
"use strict";
2
3
sodium.ready.then(function() {
4
5
const ae = new AllEars(function(ok) {
6
	if (ok) {
7
		if (localStorage.greeting) {
8
			document.getElementById("greeting").textContent = localStorage.greeting;
9
			document.getElementById("txt_pg").value = localStorage.greeting;
10
		} else localStorage.greeting = document.getElementById("greeting").textContent;
11
12
		document.getElementById("txt_skey").style.background = "#466";
13
		document.getElementById("txt_skey").maxLength = "64";
14
	} else {
15
		console.log("Failed to load All-Ears");
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
16
	}
17
});
18
19
function TabState(cur, max, btnDele, btnUpdt) {
20
	this.cur = cur;
21
	this.max = max;
22
	this.btnDele = btnDele;
23
	this.btnUpdt = btnUpdt;
24
}
25
26
const tabs = [
27
	new TabState(0, 0, false, true), // Inbox
28
	new TabState(0, 0, false, true), // Outbx
29
	new TabState(0, 1, true, false), // Write
30
	new TabState(0, 2, false, false), // Notes
31
	new TabState(0, 2, false, true) // Tools
32
];
33
34
let showHeaders = false;
35
36
let tab = 0;
37
const TAB_INBOX = 0;
38
const TAB_DRBOX = 1;
39
const TAB_WRITE = 2;
40
const TAB_NOTES = 3;
41
const TAB_TOOLS = 4;
42
43
// Helper functions
44
function getErrorMessage(err) {
45
	switch (err) {
46
		// 0x01-0x20	Client-side error codes
47
		case 0x01: return "Invalid input";
48
		case 0x02: return "Only administrators can perform this action";
49
		case 0x03: return "Failed connecting to server";
50
		case 0x04: return "Invalid input to _FetchEncrypted";
51
		case 0x05: return "Failed decrypting response from server";
52
		case 0x06: return "Invalid response length";
53
		case 0x07: return "Server responded with invalid data";
54
		case 0x08: return "Addr32 encoding failed";
55
56
		case 0x10: return "Message too short";
57
		case 0x11: return "Name too long";
58
		case 0x12: return "File too large";
59
60
		case 0x17: return "Server failed decrypting the request"; // 400
61
		case 0x18: return "Account does not exist"; // 403
62
		case 0x19: return "Server failed checking account data"; // 500
63
		case 0x20: return "Invalid status code in response";
64
65
		// 0x21-0x2F	Generic
66
		case 0x21: return ["FORMAT",    "Invalid format"];
67
		case 0x22: return ["ADMINONLY", "Only administrators can perform this action"];
68
		case 0x23: return ["MISC",      "Unknown error"];
69
		case 0x24: return ["INTERNAL",  "Internal server error"];
70
		case 0x25: return ["TODO",      "Functionality missing - in development"];
71
		case 0x26: return ["FIXME",     "Unexpected error encountered"];
72
73
		case 0x2A: return ["NOTEXIST",  "Item does not exist"];
74
75
		// 0xDA-0xDF	Address/Create|Delete|Update
76
		case 0xDA: return ["ADDRESS_CREATE_INUSE",     "Address already taken"];
77
		case 0xDB: return ["ADDRESS_CREATE_ATLIMIT",   "Limit reached - unable to register additional addresses"];
78
		case 0xDC: return ["ADDRESS_DELETE_SOMEFOUND", "Delete successful, but some addresses were not found"];
79
		case 0xDD: return ["ADDRESS_DELETE_NONEFOUND", "No such address(es)"];
80
		case 0xDE: return ["ADDRESS_UPDATE_SOMEFOUND", "Partial success - some addresses not found"];
81
		case 0xDF: return ["ADDRESS_UPDATE_NONEFOUND", "No update performed - address(es) not found"];
82
83
		// 0xE0-0xEF	Message/Create
84
		case 0xE0: return ["MESSAGE_CREATE_EXT_MINLEVEL",        "Account level too low"];
85
		case 0xE1: return ["MESSAGE_CREATE_EXT_FORMAT_FROM",     "Malformed from-address"];
86
		case 0xE2: return ["MESSAGE_CREATE_EXT_FORMAT_TO",       "Malformed to-address"];
87
		case 0xE3: return ["MESSAGE_CREATE_EXT_FORMAT_REPLYID",  "Malformed reply-id"];
88
		case 0xE4: return ["MESSAGE_CREATE_EXT_FORMAT_SUBJECT",  "Malformed subject"];
89
		case 0xE5: return ["MESSAGE_CREATE_EXT_INVALID_REPLYID", "Invalid reply-id"];
90
		case 0xE6: return ["MESSAGE_CREATE_EXT_INVALID_FROM",    "Invalid from-address"];
91
		case 0xE7: return ["MESSAGE_CREATE_EXT_INVALID_TO",      "Invalid to-address"];
92
		case 0xE8: return ["MESSAGE_CREATE_EXT_BODY_SIZE",       "Body too long or short"];
93
		case 0xE9: return ["MESSAGE_CREATE_EXT_BODY_UTF8",       "Body not UTF-8"];
94
		case 0xEA: return ["MESSAGE_CREATE_EXT_BODY_CONTROL",    "Body contains control characters"];
95
		case 0xEB: return ["MESSAGE_CREATE_EXT_LINE_TOOLONG",    "Body exceeds line-length limit"];
96
		case 0xEC: return ["MESSAGE_CREATE_EXT_BODY_FORMAT",     "Malformed body"];
97
		case 0xED: return ["MESSAGE_CREATE_EXT_BODY_TOOSHORT",   "Body too short"];
98
		case 0xEE: return ["MESSAGE_CREATE_EXT_TODOMAIN",        "Invalid to-address domain"];
99
//		case 0xEF: return ["", ""];
100
101
		// 0xF0-0xF9	Message/Create sendMail()
102
		case 0xF0: return ["MESSAGE_CREATE_SENDMAIL_GREET", "Failed greeting receiver server"];
103
		case 0xF1: return ["MESSAGE_CREATE_SENDMAIL_EHLO",  "EHLO command failed"];
104
		case 0xF2: return ["MESSAGE_CREATE_SENDMAIL_STLS",  "STARTTLS command failed"];
105
		case 0xF3: return ["MESSAGE_CREATE_SENDMAIL_SHAKE", "TLS handshake failed"];
106
		case 0xF4: return ["MESSAGE_CREATE_SENDMAIL_NOTLS", "TLS not available"];
107
		case 0xF5: return ["MESSAGE_CREATE_SENDMAIL_MAIL",  "MAIL command failed"];
108
		case 0xF6: return ["MESSAGE_CREATE_SENDMAIL_RCPT",  "RCPT command failed"];
109
		case 0xF7: return ["MESSAGE_CREATE_SENDMAIL_DATA",  "DATA command failed"];
110
		case 0xF8: return ["MESSAGE_CREATE_SENDMAIL_BODY",  "Sending body failed"];
111
//		case 0xF9: return ["", ""];
112
113
		// 0xFA-0xFF	Message/Create Int
114
		case 0xFA: return ["MESSAGE_CREATE_INT_TOOSHORT",     "Message too short"];
115
		case 0xFB: return ["MESSAGE_CREATE_INT_TS_INVALID",   "Invalid timestamp"];
116
		case 0xFC: return ["MESSAGE_CREATE_INT_SUBJECT_SIZE", "Subject too long or short"];
117
		case 0xFD: return ["MESSAGE_CREATE_INT_ADDR_NOTOWN",  "Sender address not owned"];
118
		case 0xFE: return ["MESSAGE_CREATE_INT_TO_NOTACCEPT", "Receiver address does not accept messages"];
119
		case 0xFF: return ["MESSAGE_CREATE_INT_TO_SELF",      "Sending to own account not allowed"];
120
121
		default: return ["???", "Unknown error"];
122
	}
123
}
124
125
function errorDialog(err) {
126
	if (typeof(err) !== "number" || err < 1) return;
127
128
	let btnDisable = [];
129
	const btn = document.querySelectorAll("nav > button");
130
	for (let i = 0; i < btn.length; i++) {
131
		btnDisable.push(btn[i].disabled);
132
		btn[i].disabled = true;
133
	}
134
135
	const errMsg = getErrorMessage(err);
136
137
	const dlg = document.querySelector("dialog");
138
	dlg.children[0].style.height = getComputedStyle(document.querySelector("#main1 > div[class='mid']")).height;
139
	dlg.querySelector("h1").textContent = "ERROR 0x" + err.toString(16).padStart(2, "0").toUpperCase();
140
	dlg.querySelector("p").textContent = (typeof(errMsg) === "string") ? errMsg : errMsg[1];
141
	dlg.show();
142
143
	document.querySelector("dialog > div").onclick = function() {
144
		for (let i = 0; i < btn.length; i++) {
145
			btn[i].disabled = btnDisable[i];
146
			dlg.close();
147
		}
148
	};
149
}
150
151
function getCountryName(countryCode) {
152
	switch (countryCode) {
153
		case "??": return "Unknown";
154
		case "DZ": return "Algeria";
155
		case "AO": return "Angola";
156
		case "BJ": return "Benin";
157
		case "BW": return "Botswana";
158
		case "BF": return "Burkina Faso";
159
		case "BI": return "Burundi";
160
		case "CV": return "Cabo Verde";
161
		case "CM": return "Cameroon";
162
		case "CF": return "Central African Republic";
163
		case "TD": return "Chad";
164
		case "KM": return "Comoros";
165
		case "CD": return "Congo";
166
		case "DJ": return "Djibouti";
167
		case "EG": return "Egypt";
168
		case "GQ": return "Equatorial Guinea";
169
		case "ER": return "Eritrea";
170
		case "SZ": return "Eswatini";
171
		case "ET": return "Ethiopia";
172
		case "GA": return "Gabon";
173
		case "GM": return "Gambia";
174
		case "GH": return "Ghana";
175
		case "GW": return "Guinea-Bissau";
176
		case "GN": return "Guinea";
177
		case "CI": return "Ivory Coast";
178
		case "KE": return "Kenya";
179
		case "LS": return "Lesotho";
180
		case "LR": return "Liberia";
181
		case "LY": return "Libya";
182
		case "MG": return "Madagascar";
183
		case "MW": return "Malawi";
184
		case "ML": return "Mali";
185
		case "MR": return "Mauritania";
186
		case "MU": return "Mauritius";
187
		case "YT": return "Mayotte";
188
		case "MA": return "Morocco";
189
		case "MZ": return "Mozambique";
190
		case "NA": return "Namibia";
191
		case "NE": return "Niger";
192
		case "NG": return "Nigeria";
193
		case "CG": return "Republic of the Congo";
194
		case "RW": return "Rwanda";
195
		case "RE": return "Réunion";
196
		case "SH": return "Saint Helena";
197
		case "SN": return "Senegal";
198
		case "SC": return "Seychelles";
199
		case "SL": return "Sierra Leone";
200
		case "SO": return "Somalia";
201
		case "ZA": return "South Africa";
202
		case "SS": return "South Sudan";
203
		case "SD": return "Sudan";
204
		case "ST": return "São Tomé and Príncipe";
205
		case "TZ": return "Tanzania";
206
		case "TG": return "Togo";
207
		case "TN": return "Tunisia";
208
		case "UG": return "Uganda";
209
		case "EH": return "Western Sahara";
210
		case "ZM": return "Zambia";
211
		case "ZW": return "Zimbabwe";
212
		case "AQ": return "Antarctica";
213
		case "BV": return "Bouvet Island";
214
		case "TF": return "French Southern Territories";
215
		case "HM": return "Heard Island and McDonald Islands";
216
		case "GS": return "South Georgia and the South Sandwich Islands";
217
		case "AF": return "Afghanistan";
218
		case "AM": return "Armenia";
219
		case "AZ": return "Azerbaijan";
220
		case "BH": return "Bahrain";
221
		case "BD": return "Bangladesh";
222
		case "BT": return "Bhutan";
223
		case "IO": return "British Indian Ocean Territory";
224
		case "BN": return "Brunei";
225
		case "KH": return "Cambodia";
226
		case "CN": return "China";
227
		case "CC": return "Cocos [Keeling] Islands";
228
		case "GE": return "Georgia";
229
		case "JO": return "Hashemite Kingdom of Jordan";
230
		case "HK": return "Hong Kong";
231
		case "IN": return "India";
232
		case "ID": return "Indonesia";
233
		case "IR": return "Iran";
234
		case "IQ": return "Iraq";
235
		case "IL": return "Israel";
236
		case "JP": return "Japan";
237
		case "KZ": return "Kazakhstan";
238
		case "KW": return "Kuwait";
239
		case "KG": return "Kyrgyzstan";
240
		case "LA": return "Laos";
241
		case "LB": return "Lebanon";
242
		case "MO": return "Macao";
243
		case "MY": return "Malaysia";
244
		case "MV": return "Maldives";
245
		case "MN": return "Mongolia";
246
		case "MM": return "Myanmar";
247
		case "NP": return "Nepal";
248
		case "KP": return "North Korea";
249
		case "OM": return "Oman";
250
		case "PK": return "Pakistan";
251
		case "PS": return "Palestine";
252
		case "PH": return "Philippines";
253
		case "QA": return "Qatar";
254
		case "SA": return "Saudi Arabia";
255
		case "SG": return "Singapore";
256
		case "KR": return "South Korea";
257
		case "LK": return "Sri Lanka";
258
		case "SY": return "Syria";
259
		case "TW": return "Taiwan";
260
		case "TJ": return "Tajikistan";
261
		case "TH": return "Thailand";
262
		case "TR": return "Turkey";
263
		case "TM": return "Turkmenistan";
264
		case "AE": return "United Arab Emirates";
265
		case "UZ": return "Uzbekistan";
266
		case "VN": return "Vietnam";
267
		case "YE": return "Yemen";
268
		case "AL": return "Albania";
269
		case "AD": return "Andorra";
270
		case "AT": return "Austria";
271
		case "BY": return "Belarus";
272
		case "BE": return "Belgium";
273
		case "BA": return "Bosnia and Herzegovina";
274
		case "BG": return "Bulgaria";
275
		case "HR": return "Croatia";
276
		case "CY": return "Cyprus";
277
		case "CZ": return "Czechia";
278
		case "DK": return "Denmark";
279
		case "EE": return "Estonia";
280
		case "FO": return "Faroe Islands";
281
		case "FI": return "Finland";
282
		case "FR": return "France";
283
		case "DE": return "Germany";
284
		case "GI": return "Gibraltar";
285
		case "GR": return "Greece";
286
		case "GG": return "Guernsey";
287
		case "HU": return "Hungary";
288
		case "IS": return "Iceland";
289
		case "IE": return "Ireland";
290
		case "IM": return "Isle of Man";
291
		case "IT": return "Italy";
292
		case "JE": return "Jersey";
293
		case "XK": return "Kosovo";
294
		case "LV": return "Latvia";
295
		case "LI": return "Liechtenstein";
296
		case "LU": return "Luxembourg";
297
		case "MT": return "Malta";
298
		case "MC": return "Monaco";
299
		case "ME": return "Montenegro";
300
		case "NL": return "Netherlands";
301
		case "MK": return "North Macedonia";
302
		case "NO": return "Norway";
303
		case "PL": return "Poland";
304
		case "PT": return "Portugal";
305
		case "LT": return "Republic of Lithuania";
306
		case "MD": return "Republic of Moldova";
307
		case "RO": return "Romania";
308
		case "RU": return "Russia";
309
		case "SM": return "San Marino";
310
		case "RS": return "Serbia";
311
		case "SK": return "Slovakia";
312
		case "SI": return "Slovenia";
313
		case "ES": return "Spain";
314
		case "SJ": return "Svalbard and Jan Mayen";
315
		case "SE": return "Sweden";
316
		case "CH": return "Switzerland";
317
		case "UA": return "Ukraine";
318
		case "GB": return "United Kingdom";
319
		case "VA": return "Vatican City";
320
		case "AX": return "Åland";
321
		case "AI": return "Anguilla";
322
		case "AG": return "Antigua and Barbuda";
323
		case "AW": return "Aruba";
324
		case "BS": return "Bahamas";
325
		case "BB": return "Barbados";
326
		case "BZ": return "Belize";
327
		case "BM": return "Bermuda";
328
		case "BQ": return "Bonaire, Sint Eustatius, and Saba";
329
		case "VG": return "British Virgin Islands";
330
		case "CA": return "Canada";
331
		case "KY": return "Cayman Islands";
332
		case "CR": return "Costa Rica";
333
		case "CU": return "Cuba";
334
		case "CW": return "Curaçao";
335
		case "DM": return "Dominica";
336
		case "DO": return "Dominican Republic";
337
		case "SV": return "El Salvador";
338
		case "GL": return "Greenland";
339
		case "GD": return "Grenada";
340
		case "GP": return "Guadeloupe";
341
		case "GT": return "Guatemala";
342
		case "HT": return "Haiti";
343
		case "HN": return "Honduras";
344
		case "JM": return "Jamaica";
345
		case "MQ": return "Martinique";
346
		case "MX": return "Mexico";
347
		case "MS": return "Montserrat";
348
		case "NI": return "Nicaragua";
349
		case "PA": return "Panama";
350
		case "PR": return "Puerto Rico";
351
		case "BL": return "Saint Barthélemy";
352
		case "LC": return "Saint Lucia";
353
		case "MF": return "Saint Martin";
354
		case "PM": return "Saint Pierre and Miquelon";
355
		case "VC": return "Saint Vincent and the Grenadines";
356
		case "SX": return "Sint Maarten";
357
		case "KN": return "St Kitts and Nevis";
358
		case "TT": return "Trinidad and Tobago";
359
		case "TC": return "Turks and Caicos Islands";
360
		case "VI": return "U.S. Virgin Islands";
361
		case "US": return "United States";
362
		case "AS": return "American Samoa";
363
		case "AU": return "Australia";
364
		case "CX": return "Christmas Island";
365
		case "CK": return "Cook Islands";
366
		case "TL": return "Democratic Republic of Timor-Leste";
367
		case "FM": return "Federated States of Micronesia";
368
		case "FJ": return "Fiji";
369
		case "PF": return "French Polynesia";
370
		case "GU": return "Guam";
371
		case "KI": return "Kiribati";
372
		case "MH": return "Marshall Islands";
373
		case "NR": return "Nauru";
374
		case "NC": return "New Caledonia";
375
		case "NZ": return "New Zealand";
376
		case "NU": return "Niue";
377
		case "NF": return "Norfolk Island";
378
		case "MP": return "Northern Mariana Islands";
379
		case "PW": return "Palau";
380
		case "PG": return "Papua New Guinea";
381
		case "PN": return "Pitcairn Islands";
382
		case "WS": return "Samoa";
383
		case "SB": return "Solomon Islands";
384
		case "TK": return "Tokelau";
385
		case "TO": return "Tonga";
386
		case "TV": return "Tuvalu";
387
		case "UM": return "U.S. Minor Outlying Islands";
388
		case "VU": return "Vanuatu";
389
		case "WF": return "Wallis and Futuna";
390
		case "AR": return "Argentina";
391
		case "BO": return "Bolivia";
392
		case "BR": return "Brazil";
393
		case "CL": return "Chile";
394
		case "CO": return "Colombia";
395
		case "EC": return "Ecuador";
396
		case "FK": return "Falkland Islands";
397
		case "GF": return "French Guiana";
398
		case "GY": return "Guyana";
399
		case "PY": return "Paraguay";
400
		case "PE": return "Peru";
401
		case "SR": return "Suriname";
402
		case "UY": return "Uruguay";
403
		case "VE": return "Venezuela";
404
	}
405
406
	return "Error";
407
}
408
409
function getCountryFlag(countryCode) {
410
	return (!countryCode || countryCode.length !== 2 || countryCode == "??") ? "❔" : sodium.to_string(new Uint8Array([
411
		240, 159, 135, 166 + countryCode.codePointAt(0) - 65,
412
		240, 159, 135, 166 + countryCode.codePointAt(1) - 65
413
	]));
414
}
415
416
function getClockIcon(d) {
417
	const h24 = d.getUTCHours();
418
	let h12 = (h24 === 0 ? 12 : ((h24 > 12) ? h24 - 12 : h24));
419
420
	const m60 = (d.getUTCMinutes() * 60) + d.getUTCSeconds();
421
	let m30 = 0;
422
	if (m60 <= 900) { // <= 15: round down to this hour
423
		m30 = 0;
424
	} else if (m60 > 900 && m60 < 2700) { // 15..45: round to half-past this hour
425
		m30 = 12;
426
	} else { // >= 45: round up to next hour
427
		h12++;
428
		m30 = 0;
429
	}
430
431
	return String.fromCodePoint((128335 + h12) + m30);
432
}
433
434 View Code Duplication
function shieldMix(addr) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
435
	let newAddr = "";
436
437
	for (let i = 0; i < 16; i++) {
438
		switch (addr.charAt(i)) {
439
			case '1':
440
				newAddr += "1iIlL".charAt(Math.floor(Math.random() * 5));
441
				break;
442
			case '0':
443
				newAddr += "0oO".charAt(Math.floor(Math.random() * 3));
444
				break;
445
			case 'w':
446
				newAddr += "VvWw".charAt(Math.floor(Math.random() * 4));
447
				break;
448
			default:
449
				newAddr += (Math.random() > 0.5) ? addr.charAt(i) : addr.charAt(i).toUpperCase();
450
		}
451
	}
452
453
	return newAddr;
454
}
455
456
function downloadFile(num) {
457
	const a = document.createElement("a");
458
	a.href = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
459
	a.download = ae.GetUplMsgTitle(num);
460
	a.click();
461
462
	URL.revokeObjectURL(a.href);
463
	a.href = "";
464
	a.download = "";
465
}
466
467
function clearDisplay() {
468
	let      el = document.querySelector("article > img");
469
	if (!el) el = document.querySelector("article > audio");
470
	if (!el) el = document.querySelector("article > video");
471
	if (!el) el = document.querySelector("article > embed");
472
	if (!el) el = document.querySelector("article > iframe");
473
	if (!el) return;
474
475
	URL.revokeObjectURL(el.src);
476
	el.remove();
477
}
478
479
function clearMsgFlags() {
480
	document.getElementById("readmsg_flags").children[0].innerHTML = "";
481
}
482
483
function addMsgFlag(abbr, abbrTitle) {
484
	const parent = document.getElementById("readmsg_flags").children[0];
485
486
	const el = document.createElement("abbr");
487
	el.title = abbrTitle;
488
	el.textContent = abbr;
489
490
	parent.appendChild(document.createTextNode(" "));
491
	parent.appendChild(el);
492
}
493
494
function displayFile(num) {
495
	const fileType = ae.GetUplMsgType(num);
496
	if (!fileType) {downloadFile(num); return;}
497
498
	clearDisplay();
499
	document.querySelector("article").scroll(0, 0);
500
	document.querySelector("article").setAttribute("data-msgid", ae.GetUplMsgIdHex(num));
501
502
	document.getElementById("btn_mdele").disabled = false;
503
	document.getElementById("btn_msave").disabled = false;
504
	document.getElementById("btn_reply").disabled = true;
505
506
	document.getElementById("btn_msave").onclick = function() {downloadFile(num);};
507
508
	document.querySelector("article").children[0].hidden = true;
509
	document.querySelector("article").children[1].textContent = ae.GetUplMsgTitle(num);
510
511
	switch (fileType) {
512
		case "text": {
513
			document.querySelector("article").children[2].hidden = false;
514
			document.querySelector("article").children[2].textContent = sodium.to_string(ae.GetUplMsgBody(num));
515
		break;}
516
517
		case "image": {
518
			document.querySelector("article").children[2].hidden = true;
519
			const img = document.createElement("img");
520
			img.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
521
			document.querySelector("article").appendChild(img);
522
523
			img.onclick = function() {
524
				if (!document.fullscreen)
525
					img.requestFullscreen();
526
				else
527
					document.exitFullscreen();
528
			};
529
		break;}
530
531
		case "audio":
532
		case "video": {
533
			document.querySelector("article").children[2].hidden = true;
534
			const el = document.createElement(fileType);
535
			el.controls = "controls";
536
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
537
			document.querySelector("article").appendChild(el);
538
		break;}
539
540
		case "pdf": {
541
			document.querySelector("article").children[2].hidden = true;
542
			const el = document.createElement("embed");
543
			el.type = "application/pdf";
544
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer], {type: "application/pdf"}));
545
			document.querySelector("article").appendChild(el);
546
		break;}
547
548
		case "html": {
549
			document.querySelector("article").children[2].hidden = true;
550
			const el = document.createElement("iframe");
551
			el.allow = "";
552
			el.sandbox = "";
553
			el.referrerPolicy = "no-referrer";
554
			el.csp = "base-uri 'none'; child-src 'none'; connect-src 'none'; default-src 'none'; font-src 'none'; form-action 'none'; frame-ancestors 'none'; frame-src 'none'; img-src 'none'; manifest-src 'none'; media-src 'none'; object-src 'none'; script-src 'none'; style-src 'none'; worker-src 'none';";
555
			el.srcdoc = sodium.to_string(ae.GetUplMsgBody(num).buffer);
556
			document.querySelector("article").appendChild(el);
557
		break;}
558
	}
559
}
560
561
function displayMsg(isInt, num) {
562
	clearDisplay();
563
564
	document.getElementById("btn_mdele").disabled = false;
565
	document.getElementById("btn_msave").disabled = isInt;
566
567
	document.querySelector("article").scroll(0, 0);
568
	document.querySelector("article").setAttribute("data-msgid", isInt? ae.GetIntMsgIdHex(num) : ae.GetExtMsgIdHex(num));
569
570
	const ts = isInt? ae.GetIntMsgTime(num) : ae.GetExtMsgTime(num);
571
572
	if (!isInt) {
573
		document.getElementById("btn_msave").onclick = function() {
574
			this.blur();
575
576
			const a = document.createElement("a");
577
			a.href = URL.createObjectURL(new Blob([ae.ExportExtMsg(num)]));
578
			a.download = ae.GetExtMsgTitle(num);
579
			a.click();
580
581
			URL.revokeObjectURL(a.href);
582
			a.href = "";
583
			a.download = "";
584
		};
585
	}
586
587
	if (!isInt || (ae.GetIntMsgFrom(num) !== "public" && ae.GetIntMsgFrom(num) !== "system")) {
588
		document.getElementById("btn_reply").disabled = false;
589
590
		document.getElementById("btn_reply").onclick = function() {
591
			document.getElementById("write_recv").value = isInt? ae.GetIntMsgFrom(num) : ae.GetExtMsgReplyAddress(num);
592
			document.getElementById("write_subj").value = isInt? ae.GetIntMsgTitle(num) : ae.GetExtMsgTitle(num);
593
			if (!document.getElementById("write_subj").value.startsWith("Re:")) document.getElementById("write_subj").value = "Re: " + document.getElementById("write_subj").value;
594
			document.querySelector("#write2_pkey > input").value = isInt? ae.GetIntMsgFromPk(num) : "";
595
596
			document.getElementById("write_recv").readOnly = !isInt;
597
			document.getElementById("write_subj").readOnly = !isInt;
598
			document.getElementById("write_subj").setAttribute("data-replyid", isInt? "" : ae.GetExtMsgHdrId(num));
599
600
			tabs[TAB_WRITE].cur = 0;
601
			document.getElementById("btn_write").disabled = false;
602
			document.getElementById("btn_write").click();
603
			document.getElementById("write_body").focus();
604
605
			for (const opt of document.getElementById("write_from").options) {
606
				if (opt.value === (isInt ? ae.GetIntMsgTo(num) : ae.GetExtMsgEnvTo(num).split("@")[0].toLowerCase())) {
607
					opt.selected = true;
608
				}
609
			}
610
		};
611
	} else {
612
		document.getElementById("btn_reply").disabled = true;
613
	}
614
615
	document.querySelector("article").children[0].hidden = false;
616
	document.querySelector("article").children[2].hidden = false;
617
618
	document.getElementById("readmsg_envto").textContent = isInt ? "" : ae.GetExtMsgEnvTo(num);
619
	document.getElementById("readmsg_hdrto").textContent = isInt ? ae.GetIntMsgTo(num) : (ae.GetExtMsgHdrTo(num) + (ae.GetExtMsgDnTo(num) ? " (" + ae.GetExtMsgDnTo(num) + ")" : ""));
620
621
	const tzOs = new Date().getTimezoneOffset();
622
	const msgDate = new Date((ts * 1000) + (tzOs * -60000));
623
	document.getElementById("readmsg_date").children[0].textContent = getClockIcon(msgDate);
624
	document.getElementById("readmsg_date").children[1].dateTime = new Date(ts * 1000).toISOString();
625
626
	if (isInt) {
627
		document.querySelector("article").children[1].textContent = ae.GetIntMsgTitle(num);
628
		document.querySelector("article").children[2].textContent = ae.GetIntMsgBody(num);
629
630
		document.getElementById("readmsg_date").children[1].textContent = msgDate.toISOString().slice(0, 19).replace("T", " ");
631
632
		document.getElementById("readmsg_ip").style.visibility = "hidden";
633
		document.getElementById("readmsg_rdns").style.visibility = "hidden";
634
		document.getElementById("readmsg_dkim").style.visibility = "hidden";
635
		document.getElementById("readmsg_greet").style.visibility = "hidden";
636
		document.getElementById("readmsg_cert").style.visibility = "hidden";
637
		document.getElementById("readmsg_envfrom").style.visibility = "hidden";
638
		document.getElementById("readmsg_envto").style.visibility = "hidden";
639
640
		if (ae.GetIntMsgFrom(num) !== "system" && ae.GetIntMsgFrom(num) !== "public") {
641
			document.getElementById("readmsg_tls").style.visibility = "visible";
642
			document.getElementById("readmsg_tls").children[0].textContent = ae.GetIntMsgFromPk(num);
643
		} else document.getElementById("readmsg_tls").style.visibility = "hidden";
644
645
		let symbol = document.createElement("span");
646
		if      (ae.GetIntMsgLevel(num) === 3 && ae.GetIntMsgFrom(num) === "system") {symbol.title = "System message"; symbol.textContent = "🅢";}
647
		else if (ae.GetIntMsgLevel(num) === 3 && ae.GetIntMsgFrom(num) === "public") {symbol.title = "Public announcement"; symbol.textContent = "🅟";}
648
		else if (ae.GetIntMsgLevel(num) === 3) {symbol.title = "Administrator"; symbol.textContent = "🅐";}
649
		else if (ae.GetIntMsgLevel(num) === 2) {symbol.title = "Level 2";  symbol.textContent = "➋";}
650
		else if (ae.GetIntMsgLevel(num) === 1) {symbol.title = "Level 1";  symbol.textContent = "➊";}
651
		else if (ae.GetIntMsgLevel(num) === 0) {symbol.title = "Level 0";  symbol.textContent = "🄌";}
652
		else {symbol.title = "Invalid level"; symbol.textContent = "⚠";}
653
654
		const elHdrFrom = document.getElementById("readmsg_hdrfrom");
655
		elHdrFrom.innerHTML = "";
656
		elHdrFrom.appendChild(symbol);
657
		elHdrFrom.appendChild(document.createTextNode(" " + ae.GetIntMsgFrom(num)));
658
659
		clearMsgFlags();
660
		if (!ae.GetIntMsgFlagVPad(num)) addMsgFlag("PAD", "Invalid padding");
661
		if (!ae.GetIntMsgFlagVSig(num)) addMsgFlag("SIG", "Invalid signature");
662
		if ( ae.GetIntMsgFlagE2ee(num)) addMsgFlag("E2EE", "End-to-end encrypted");
663
	} else {
664
		document.querySelector("article").children[2].innerHTML = "";
665
666
		const headers = document.createElement("p");
667
		headers.textContent = ae.GetExtMsgHeaders(num);
668
		headers.className = "mono";
669
		headers.hidden = !showHeaders;
670
		document.querySelector("article").children[2].appendChild(headers);
671
672
		const body = document.createElement("p");
673
		body.innerHTML = ae.GetExtMsgBody(num);
674
		document.querySelector("article").children[2].appendChild(body);
675
676
		document.querySelector("article").children[1].textContent = ae.GetExtMsgTitle(num);
677
		document.querySelector("article").children[1].style.cursor = headers.textContent? "pointer" : "";
678
		document.querySelector("article").children[1].onclick = function() {
679
			if (!headers.textContent) return;
680
			showHeaders = !showHeaders;
681
			headers.hidden = !showHeaders;
682
		};
683
684
		let hdrSecs = Math.abs(ae.GetExtMsgHdrTime(num));
685
		let hdrTime = "";
686
		if (hdrSecs >= 3600) {
687
			const hdrHours = Math.floor(hdrSecs / 3600);
688
			hdrTime += hdrHours.toString() + "h ";
689
			hdrSecs -= hdrHours * 3600;
690
		}
691
		if (hdrSecs >= 60) {
692
			const hdrMins = Math.floor(hdrSecs / 60);
693
			hdrTime += hdrMins.toString() + "m ";
694
			hdrSecs -= hdrMins * 60;
695
		}
696
		hdrTime += hdrSecs + "s";
697
698
		const hdrTz = (ae.GetExtMsgHdrTz(num) >= 0 ? "+" : "-") + Math.floor(Math.abs(ae.GetExtMsgHdrTz(num)) / 60).toString().padStart(2, "0") + (Math.abs(ae.GetExtMsgHdrTz(num)) % 60).toString().padStart(2, "0");
699
		document.getElementById("readmsg_date").children[1].textContent = msgDate.toISOString().slice(0, 19).replace("T", " ") + "; " + hdrTz + " " + ((ae.GetExtMsgHdrTime(num) >= 0) ? "+" : "-") + hdrTime;
700
701
		document.getElementById("readmsg_ip").style.visibility = "visible";
702
		document.getElementById("readmsg_rdns").style.visibility = "visible";
703
		document.getElementById("readmsg_dkim").style.visibility = "visible";
704
		document.getElementById("readmsg_greet").style.visibility = "visible";
705
		document.getElementById("readmsg_tls").style.visibility = "visible";
706
		document.getElementById("readmsg_cert").style.visibility = "visible";
707
		document.getElementById("readmsg_envfrom").style.visibility = "visible";
708
		document.getElementById("readmsg_envto").style.visibility = "visible";
709
710
		const cc = ae.GetExtMsgCountry(num);
711
712
		// DKIM
713
		let dkim = "";
714
		if (ae.GetExtMsgDkim(num)) {
715
			[ // Look for a matching domain in this order
716
				ae.GetExtMsgHdrFrom(num).split("@")[1],
717
				ae.GetExtMsgEnvFrom(num).split("@")[1],
718
				ae.GetExtMsgRdns(num),
719
				ae.GetExtMsgGreet(num),
720
				ae.GetExtMsgTlsDomain(num)
721
			].forEach(function(dom) {
722
				if (dkim) return;
723
				for (let i = 0; i < ae.GetExtMsgDkim(num).domain.length; i++) {
724
					if (ae.GetExtMsgDkim(num).domain[i] === dom) {
725
						dkim = dom + " ✓";
726
						return;
727
					}
728
				}
729
			});
730
731
			if (!dkim) dkim = ae.GetExtMsgDkim(num).domain[0]; // Default to first signature domain
732
		}
733
734
		if (ae.GetExtMsgFlagDkFl(num)) dkim += " (fail)";
735
736
		// Left side
737
		document.getElementById("readmsg_country").textContent = getCountryFlag(cc);
738
		document.getElementById("readmsg_country").title = getCountryName(cc);
739
		document.getElementById("readmsg_ip").children[1].textContent = ae.GetExtMsgIp(num) + (ae.GetExtMsgFlagIpBl(num) ? " ❗" : "");
740
		document.getElementById("readmsg_tls").children[0].textContent = ae.GetExtMsgTLS(num);
741
742
		// Right side
743
		document.getElementById("readmsg_greet").children[0].textContent = ae.GetExtMsgGreet(num) + (ae.GetExtMsgFlagGrDm(num) ? " ✓" : "");
744
		document.getElementById("readmsg_rdns").children[0].textContent = ae.GetExtMsgRdns(num) + (ae.GetExtMsgGreet(num) === ae.GetExtMsgRdns(num) ? " ✓" : "");
745
		document.getElementById("readmsg_cert").children[0].textContent = ae.GetExtMsgTlsDomain(num) ? (ae.GetExtMsgTlsDomain(num) + " ✓") : "";
746
		document.getElementById("readmsg_dkim").children[0].textContent = dkim;
747
		document.getElementById("readmsg_envfrom").textContent = ae.GetExtMsgEnvFrom(num);
748
		document.getElementById("readmsg_hdrfrom").textContent = ae.GetExtMsgHdrFrom(num) + (ae.GetExtMsgDnFrom(num) ? " (" + ae.GetExtMsgDnFrom(num) + ")" : "");
749
750
		clearMsgFlags();
751
		if (!ae.GetExtMsgFlagVPad(num)) addMsgFlag("PAD", "Invalid padding");
752
		if (!ae.GetExtMsgFlagVSig(num)) addMsgFlag("SIG", "Invalid signature");
753
		if (!ae.GetExtMsgFlagPExt(num)) addMsgFlag("SMTP", "The sender did not use the Extended (ESMTP) protocol");
754
		if (!ae.GetExtMsgFlagQuit(num)) addMsgFlag("QUIT", "The sender did not issue the required QUIT command");
755
		if ( ae.GetExtMsgFlagRare(num)) addMsgFlag("RARE", "The sender issued unusual command(s)");
756
		if ( ae.GetExtMsgFlagFail(num)) addMsgFlag("FAIL", "The sender issued invalid command(s)");
757
		if ( ae.GetExtMsgFlagPErr(num)) addMsgFlag("PROT", "The sender violated the protocol");
758
	}
759
}
760
761
function displayOutMsg(num) {
762
	clearDisplay();
763
	document.querySelector("article").scroll(0, 0);
764
	document.querySelector("article").setAttribute("data-msgid", ae.GetOutMsgIdHex(num));
765
766
	document.getElementById("btn_mdele").disabled = false;
767
	document.getElementById("btn_msave").disabled = true;
768
	document.getElementById("btn_reply").disabled = true;
769
770
	document.querySelector("article").children[0].hidden = false;
771
	document.querySelector("article").children[2].hidden = false;
772
773
	document.querySelector("article").children[1].textContent = ae.GetOutMsgSubj(num);
774
	document.querySelector("article").children[2].textContent = ae.GetOutMsgBody(num);
775
776
	document.getElementById("readmsg_dkim").style.visibility    = "hidden";
777
	document.getElementById("readmsg_hdrto").style.visibility   = "visible";
778
	document.getElementById("readmsg_hdrfrom").style.visibility = "visible";
779
	document.getElementById("readmsg_envto").style.visibility   = "visible";
780
	document.getElementById("readmsg_envfrom").style.visibility = "hidden";
781
782
	document.getElementById("readmsg_hdrfrom").textContent = ae.GetOutMsgFrom(num);
783
784
	document.getElementById("readmsg_envto").textContent = ae.GetOutMsgMxDom(num);
785
	document.getElementById("readmsg_hdrto").textContent = ae.GetOutMsgTo(num);
786
787
	const ts = ae.GetOutMsgTime(num);
788
	const tzOs = new Date().getTimezoneOffset();
789
	document.getElementById("readmsg_date").children[1].textContent = new Date((ts * 1000) + (tzOs * -60000)).toISOString().slice(0, 19).replace("T", " ");
790
791
	const isInt = ae.GetOutMsgIsInt(num);
792
	document.getElementById("readmsg_ip").style.visibility    = isInt? "hidden" : "visible";
793
	document.getElementById("readmsg_rdns").style.visibility  = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
794
	document.getElementById("readmsg_tls").style.visibility   = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
795
	document.getElementById("readmsg_cert").style.visibility  = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
796
	document.getElementById("readmsg_greet").style.visibility = isInt? "hidden" : "visible";
797
798
	if (!isInt) {
799
//		const cc = ae.GetExtMsgCountry(num);
800
801
		document.getElementById("readmsg_ip").children[1].textContent = ae.GetOutMsgIp(num);
802
//		document.getElementById("readmsg_country").textContent = getCountryFlag(cc) + " " + getCountryName(cc);
803
//		document.getElementById("readmsg_tls").children[0].textContent = ae.GetOutMsgTLS(num);
804
		document.getElementById("readmsg_greet").children[0].textContent = ae.GetOutMsgGreet(num);
805
	}
806
807
	clearMsgFlags();
808
	if (!ae.GetOutMsgFlagVPad(num)) addMsgFlag("PAD", "Invalid padding");
809
	if (!ae.GetOutMsgFlagVSig(num)) addMsgFlag("SIG", "Invalid signature");
810
	if ( ae.GetOutMsgFlagE2ee(num)) addMsgFlag("E2EE", "End-to-end encrypted");
811
}
812
813
function addSent() {
814
	const tbl = document.getElementById("tbl_drbox");
815
	tbl.innerHTML = "";
816
817
	for (let i = 0; i < ae.GetOutMsgCount(); i++) {
818
		const row = tbl.insertRow(-1);
819
		row.setAttribute("data-msgid", ae.GetOutMsgIdHex(i));
820
821
		let cell;
822
		cell = row.insertCell(-1); cell.textContent = new Date(ae.GetOutMsgTime(i) * 1000).toISOString().slice(0, 10);
823
		cell = row.insertCell(-1); cell.textContent = ae.GetOutMsgSubj(i);
824
		row.onclick = function() {displayOutMsg(i);};
825
	}
826
}
827
828
function updateAddressCounts() {
829
	document.querySelector("#tbd_accs > tr > td:nth-child(3)").textContent = ae.GetAddressCountNormal();
830
	document.querySelector("#tbd_accs > tr > td:nth-child(4)").textContent = ae.GetAddressCountShield();
831
832
	document.getElementById("limit_normal").textContent = (ae.GetAddressCountNormal() + "/" + ae.GetLimitNormalA(ae.GetUserLevel())).padStart(ae.GetLimitNormalA(ae.GetUserLevel()) > 9 ? 5 : 1);
833
	document.getElementById("limit_shield").textContent = (ae.GetAddressCountShield() + "/" + ae.GetLimitShieldA(ae.GetUserLevel())).padStart(ae.GetLimitShieldA(ae.GetUserLevel()) > 9 ? 5 : 1);
834
	document.getElementById("limit_total").textContent = ((ae.GetAddressCountNormal() + ae.GetAddressCountShield()) + "/" + ae.GetAddrPerUser()).padStart(5);
835
836
	const limitReached = (ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31);
837
	document.getElementById("btn_address_create_normal").disabled = (limitReached || ae.GetAddressCountNormal() >= ae.GetLimitNormalA(ae.GetUserLevel()));
838
	document.getElementById("btn_address_create_shield").disabled = (limitReached || ae.GetAddressCountShield() >= ae.GetLimitShieldA(ae.GetUserLevel()));
839
}
840
841
function adjustLevel(pubkey, level, c) {
842
	const fs = document.getElementById("tbl_accs");
843
	fs.disabled = true;
844
845
	ae.Account_Update(pubkey, level, function(error) {
846
		fs.disabled = false;
847
848
		if (error === 0) {
849
			c[4].textContent = level;
850
			c[5].children[0].disabled = (level === 3);
851
			c[6].children[0].disabled = (level === 0);
852
		} else errorDialog(error);
853
	});
854
}
855
856
function addMsg(isInt, i) {
857
	const row = document.getElementById("tbl_inbox").insertRow(-1);
858
	row.setAttribute("data-msgid", isInt? ae.GetIntMsgIdHex(i) : ae.GetExtMsgIdHex(i));
859
860
	const ts = isInt? ae.GetIntMsgTime(i) : ae.GetExtMsgTime(i);
861
	const el = document.createElement("time");
862
	el.dateTime = new Date(ts * 1000).toISOString();
863
	el.textContent = new Date((ts * 1000) + (new Date().getTimezoneOffset() * -60000)).toISOString().slice(0, 10);
864
	let cell = row.insertCell(-1);
865
	cell.appendChild(el);
866
867
	cell = row.insertCell(-1);
868
	cell.textContent = isInt? ae.GetIntMsgTitle(i) : ae.GetExtMsgTitle(i);
869
870
	if (isInt) {
871
		cell = row.insertCell(-1);
872
		cell.textContent = ae.GetIntMsgFrom(i);
873
		cell.className = (ae.GetIntMsgFrom(i).length === 16) ? "mono" : "";
874
	} else {
875
		const from1 = ae.GetExtMsgHdrFrom(i);
876
		const from2 = from1.substring(from1.indexOf("@") + 1);
877
		cell = row.insertCell(-1);
878
		cell.textContent = from1.substring(0, from1.indexOf("@"));
879
880
		const flag = document.createElement("abbr");
881
		const cc = ae.GetExtMsgCountry(i);
882
		flag.textContent = getCountryFlag(cc);
883
		flag.title = getCountryName(cc);
884
885
		const fromText = document.createElement("span");
886
		fromText.textContent = " " + from2;
887
888
		cell = row.insertCell(-1);
889
		cell.appendChild(flag);
890
		cell.appendChild(fromText);
891
	}
892
893
	row.onclick = function() {
894
		displayMsg(isInt, i);
895
	};
896
}
897
898
function getRowsPerPage() {
899
	const tbl = document.getElementById("tbl_inbox");
900
	tbl.innerHTML = "";
901
	const row = tbl.insertRow(-1);
902
	const cell = row.insertCell(-1);
903
	cell.textContent = "0";
904
905
	const rowsPerPage = Math.floor(getComputedStyle(document.getElementById("div_inbox")).height.replace("px", "") / getComputedStyle(document.querySelector("#tbl_inbox > tbody > tr:first-child")).height.replace("px", "")) - 1; // -1 allows space for 'load more'
906
	tbl.innerHTML = "";
907
	return rowsPerPage;
908
}
909
910
function addMessages() {
911
	const maxExt = ae.GetExtMsgCount();
912
	const maxInt = ae.GetIntMsgCount();
913
914
	if (maxExt + maxInt < 1) {
915
		tabs[TAB_INBOX].max = 0;
916
		return;
917
	}
918
919
	const rowsPerPage = getRowsPerPage();
920
	let skipMsgs = rowsPerPage * tabs[TAB_INBOX].cur;
921
922
	tabs[TAB_INBOX].max = Math.floor((maxExt + maxInt - 1) / rowsPerPage);
923
924
	let numExt = 0;
925
	let numInt = 0;
926
	let numAdd = 0;
927
928
	while (numAdd < rowsPerPage) {
929
		const tsInt = (numInt < maxInt) ? ae.GetIntMsgTime(numInt) : -1;
930
		const tsExt = (numExt < maxExt) ? ae.GetExtMsgTime(numExt) : -1;
931
		if (tsInt === -1 && tsExt === -1) break;
932
933
		if (tsInt !== -1 && (tsExt === -1 || tsInt > tsExt)) {
934
			if (skipMsgs > 0) skipMsgs--; else {addMsg(true, numInt); numAdd++;}
935
			numInt++;
936
		} else if (tsExt !== -1) {
937
			if (skipMsgs > 0) skipMsgs--; else {addMsg(false, numExt); numAdd++;}
938
			numExt++;
939
		}
940
	}
941
942
	if (ae.GetReadyMsgBytes() < ae.GetTotalMsgBytes()) {
943
		const inbox = document.getElementById("tbl_inbox");
944
		const row = inbox.insertRow(-1);
945
		const cell = row.insertCell(-1);
946
		cell.textContent = "Load more (" + Math.round((ae.GetTotalMsgBytes() - ae.GetReadyMsgBytes()) / 1024) + " KiB left)";
947
948
		row.onclick = function() {
949
			this.onclick = "";
950
951
			ae.Message_Browse(false, false, function(errorBrowse) {
952
				document.getElementById("tbl_inbox").style.opacity = 1;
953
954
				if (errorBrowse === 0) {
955
					addMessages();
956
					addUploads();
957
					addSent();
958
					if (tabs[tab].cur < tabs[tab].max) document.getElementById("btn_rght").disabled = false;
959
				} // else TODO
960
			});
961
		};
962
	}
963
}
964
965
function addUploads() {
966
	const tbl = document.getElementById("tbd_uploads");
967
	tbl.innerHTML = "";
968
969
	for (let i = 0; i < ae.GetUplMsgCount(); i++) {
970
		const row = tbl.insertRow(-1);
971
		row.setAttribute("data-msgid", ae.GetUplMsgIdHex(i));
972
973
		let cell;
974
		cell = row.insertCell(-1); cell.textContent = new Date(ae.GetUplMsgTime(i) * 1000).toISOString().slice(0, 10);
975
976
		cell = row.insertCell(-1); cell.textContent = ae.GetUplMsgTitle(i);
977
		cell.onclick = function() {displayFile(this.parentElement.rowIndex - 1);};
978
979
		cell = row.insertCell(-1); cell.textContent = (ae.GetUplMsgBytes(i) / 1024).toFixed(1);
980
981
		cell = row.insertCell(-1);
982
		if (ae.GetUplMsgIdHex(i)) {
983
			const btn = document.createElement("button");
984
			btn.setAttribute("data-msgid", ae.GetUplMsgIdHex(i));
985
			btn.type = "button";
986
			btn.textContent = "X";
987
988
			btn.onclick = function() {
989
				const tr = this.parentElement.parentElement;
990
				ae.Message_Delete(this.getAttribute("data-msgid"), function(error) {
991
					if (error === 0) tr.remove();
992
					else errorDialog(error);
993
				});
994
			};
995
996
			cell.appendChild(btn);
997
		}
998
	}
999
}
1000
1001
function addAccountToTable(i) {
1002
	const tblAccs = document.getElementById("tbd_accs");
1003
	const row = tblAccs.insertRow(-1);
1004
	let cell;
1005
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserPkHex(i);
1006
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserSpace(i);
1007
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserNAddr(i);
1008
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserSAddr(i);
1009
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserLevel(i);
1010
1011
	cell = row.insertCell(-1);
1012
	let btn = document.createElement("button");
1013
	btn.type = "button";
1014
	btn.textContent = "+";
1015
	btn.disabled = (ae.Admin_GetUserLevel(i) === 3);
1016
	btn.onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[4].textContent, 10) + 1, c);};
1017
	cell.appendChild(btn);
1018
1019
	cell = row.insertCell(-1);
1020
	btn = document.createElement("button");
1021
	btn.type = "button";
1022
	btn.textContent = "−";
1023
	btn.disabled = (ae.Admin_GetUserLevel(i) === 0);
1024
	btn.onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[4].textContent, 10) - 1, c);};
1025
	cell.appendChild(btn);
1026
1027
	cell = row.insertCell(-1);
1028
	btn = document.createElement("button");
1029
	btn.type = "button";
1030
	btn.textContent = "X";
1031
	btn.onclick = function() {
1032
		const tr = this.parentElement.parentElement;
1033
		ae.Account_Delete(tr.cells[0].textContent, function(error) {
1034
			if (error === 0) tr.remove(); else errorDialog(error);
1035
		});
1036
	};
1037
	cell.appendChild(btn);
1038
}
1039
1040
function reloadAccount() {
1041
	// Limits
1042
	const tblLimits = document.getElementById("tbl_limits");
1043
	if (ae.IsUserAdmin()) {
1044
		for (let i = 0; i < 4; i++) {
1045
			tblLimits.rows[i].cells[1].children[0].disabled = false;
1046
			tblLimits.rows[i].cells[2].children[0].disabled = false;
1047
			tblLimits.rows[i].cells[3].children[0].disabled = false;
1048
1049
			tblLimits.rows[i].cells[1].children[0].value = ae.GetLimitStorage(i) + 1;
1050
			tblLimits.rows[i].cells[2].children[0].value = ae.GetLimitNormalA(i);
1051
			tblLimits.rows[i].cells[3].children[0].value = ae.GetLimitShieldA(i);
1052
		}
1053
	} else {
1054
		const lvl = ae.GetUserLevel();
1055
		tblLimits.rows[lvl].cells[1].children[0].value = ae.GetLimitStorage(lvl) + 1;
1056
		tblLimits.rows[lvl].cells[2].children[0].value = ae.GetLimitNormalA(lvl);
1057
		tblLimits.rows[lvl].cells[3].children[0].value = ae.GetLimitShieldA(lvl);
1058
	}
1059
1060
	// Accounts
1061
	const tblAccs = document.getElementById("tbd_accs");
1062
1063
	// All: Our account
1064
	const row = tblAccs.insertRow(-1);
1065
	let cell;
1066
	cell = row.insertCell(-1); cell.textContent = ae.GetUserPkHex();
1067
	cell = row.insertCell(-1); cell.textContent = Math.round(ae.GetTotalMsgBytes() / 1048576); // MiB
1068
	cell = row.insertCell(-1); cell.textContent = ae.GetAddressCountNormal();
1069
	cell = row.insertCell(-1); cell.textContent = ae.GetAddressCountShield();
1070
	cell = row.insertCell(-1); cell.textContent = ae.GetUserLevel();
1071
1072
	cell = row.insertCell(-1);
1073
	let btn = document.createElement("button");
1074
	btn.type = "button";
1075
	btn.textContent = "+";
1076
	btn.disabled = true;
1077
	cell.appendChild(btn);
1078
1079
	cell = row.insertCell(-1);
1080
	btn = document.createElement("button");
1081
	btn.type = "button";
1082
	btn.textContent = "−";
1083
	btn.disabled = true;
1084
	btn.id = "btn_lowme";
1085
	btn.onclick = function() {
1086
		const newLevel = parseInt(row.cells[4].textContent, 10) - 1;
1087
		ae.Account_Update(ae.GetUserPkHex(), newLevel, function(error) {
1088
			if (error === 0) {
1089
				row.cells[4].textContent = newLevel;
1090
				if (newLevel === 0) {
1091
					document.getElementById("btn_lowme").disabled = true;
1092
					document.getElementById("chk_lowme").disabled = true;
1093
				}
1094
			} else errorDialog(error);
1095
		});
1096
	};
1097
	cell.appendChild(btn);
1098
1099
	cell = row.insertCell(-1);
1100
	btn = document.createElement("button");
1101
	btn.type = "button";
1102
	btn.textContent = "X";
1103
	btn.disabled = true;
1104
	btn.id = "btn_delme";
1105
	btn.onclick = function() {
1106
		ae.Account_Delete(ae.GetUserPkHex(), function(error) {
1107
			if (error === 0) {
1108
				row.remove();
1109
				document.getElementById("chk_delme").disabled = true;
1110
			} else errorDialog(error);
1111
		});
1112
	};
1113
	cell.appendChild(btn);
1114
1115
	document.getElementById("txt_reg").disabled = !ae.IsUserAdmin();
1116
	document.getElementById("btn_reg").disabled = !ae.IsUserAdmin();
1117
	document.getElementById("chk_lowme").disabled = (ae.GetUserLevel() === 0);
1118
1119
	// Contacts
1120
	for (let i = 0; i < ae.GetContactCount(); i++) {
1121
		addContact(
1122
			ae.GetContactMail(i),
1123
			ae.GetContactName(i),
1124
			ae.GetContactNote(i)
1125
		);
1126
	}
1127
1128
	refreshContactList();
1129
1130
	// Addresses
1131
	for (let i = 0; i < ae.GetAddressCount(); i++) {
1132
		addAddress(i);
1133
	}
1134
1135
	updateAddressCounts();
1136
	addMessages();
1137
	addUploads();
1138
	addSent();
1139
1140
	document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1141
}
1142
1143
function deleteAddress(addr) {
1144
	let btns = document.querySelectorAll("#tbl_addrs button");
1145
	for (let i = 0; i < btns.length; i++) btns[i].disabled = true;
1146
1147
	let addressToDelete = -1;
1148
1149
	for (let i = 0; i < ae.GetAddressCount(); i++) {
1150
		if (addr === ae.GetAddress(i)) {
1151
			addressToDelete = i;
1152
			break;
1153
		}
1154
	}
1155
1156
	if (addressToDelete === -1) return;
1157
1158
	ae.Address_Delete(addressToDelete, function(error1) {
1159
		if (error1 === 0) {
1160
			document.getElementById("tbl_addrs").deleteRow(addressToDelete);
1161
			document.getElementById("write_from").remove(addressToDelete);
1162
			updateAddressCounts();
1163
1164
			const limitReached = (ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31);
1165
			document.getElementById("btn_address_create_normal").disabled = (limitReached || ae.GetAddressCountNormal() > ae.GetLimitNormalA(ae.GetUserLevel()));
1166
			document.getElementById("btn_address_create_shield").disabled = (limitReached || ae.GetAddressCountShield() > ae.GetLimitShieldA(ae.GetUserLevel()));
1167
1168
			ae.Private_Update(function(error2) {
1169
				btns = document.querySelectorAll("#tbl_addrs button");
1170
				for (let i = 0; i < btns.length; i++) btns[i].disabled = false;
1171
				if (error2) errorDialog(error2);
1172
			});
1173
		} else {
1174
			btns = document.querySelectorAll("#tbl_addrs button");
1175
			for (let i = 0; i < btns.length; i++) btns[i].disabled = false;
1176
			errorDialog(error1);
1177
		}
1178
	});
1179
}
1180
1181
function addAddress(num) {
1182
	const addrTable = document.getElementById("tbl_addrs");
1183
	const row = addrTable.insertRow(-1);
1184
	const addr = ae.GetAddress(num);
1185
1186
	let cell = row.insertCell(-1);
1187
	cell.textContent = addr;
1188
	cell.onclick = function() {navigator.clipboard.writeText(((this.textContent.length === 16) ? shieldMix(this.textContent) : this.textContent) + "@" + ae.GetDomainEml());};
1189
1190
	cell = row.insertCell(-1);
1191
	let el = document.createElement("input");
1192
	el.type = "checkbox";
1193
	el.checked = ae.GetAddressAccExt(num);
1194
	cell.appendChild(el);
1195
1196
	cell = row.insertCell(-1);
1197
	el = document.createElement("input");
1198
	el.type = "checkbox";
1199
	el.checked = ae.GetAddressAccInt(num);
1200
	cell.appendChild(el);
1201
1202
	cell = row.insertCell(-1);
1203
	el = document.createElement("button");
1204
	el.type = "button";
1205
	el.textContent = "X";
1206
	el.onclick = function() {deleteAddress(addr);};
1207
	cell.appendChild(el);
1208
1209
	el = document.createElement("option");
1210
	el.value = addr;
1211
	el.textContent = addr + "@" + ae.GetDomainEml();
1212
	document.getElementById("write_from").appendChild(el);
1213
}
1214
1215
// Interface
1216
document.getElementById("btn_dele").onclick = function() {
1217
	this.blur();
1218
1219
	if (tab === TAB_WRITE) {
1220
		tabs[tab].cur = 0;
1221
		updateTab();
1222
1223
		document.querySelector("#write2_pkey > input").value = "";
1224
1225
		document.getElementById("write_recv").value = "";
1226
		document.getElementById("write_subj").value = "";
1227
		document.getElementById("write_body").value = "";
1228
1229
		document.getElementById("write_recv").readOnly = false;
1230
		document.getElementById("write_subj").readOnly = false;
1231
		document.getElementById("write_subj").setAttribute("data-replyid", "");
1232
1233
		document.getElementById("write_recv").focus();
1234
	}
1235
};
1236
1237
document.getElementById("btn_updt").onclick = function() {
1238
	const btn = this;
1239
	btn.disabled = true;
1240
	btn.blur();
1241
1242
	if (tab === TAB_INBOX) {
1243
		document.getElementById("tbl_inbox").style.opacity = 0.5;
1244
1245
		ae.Message_Browse(true, false, function(error) {
1246
			if (error === 0) {
1247
				addMessages();
1248
				addUploads();
1249
			} else {
1250
				errorDialog(error);
1251
			}
1252
1253
			document.getElementById("tbl_inbox").style.opacity = 1;
1254
			btn.disabled = false;
1255
		});
1256
	}
1257
};
1258
1259
document.getElementById("btn_mdele").onclick = function() {
1260
	const btn = this;
1261
	btn.blur();
1262
	btn.disabled = true;
1263
1264
	const delId = document.querySelector("article").getAttribute("data-msgid");
1265
	if (!delId) return;
1266
1267
	ae.Message_Delete(delId, function(error) {
1268
		if (error === 0) {
1269
			["tbl_inbox", "tbl_drbox", "tbd_uploads"].forEach(function(tbl_name) {
1270
				const tbl = document.getElementById(tbl_name);
1271
				for (let i = 0; i < tbl.rows.length; i++) {if (tbl.rows[i].getAttribute("data-msgid") === delId) tbl.deleteRow(i);}
1272
			});
1273
1274
			addMessages();
1275
			addUploads();
1276
			addSent();
1277
		} else btn.disabled = false; // TODO display error
1278
	});
1279
};
1280
1281
function refreshContactList() {
1282
	const lst = document.getElementById("contact_emails");
1283
	lst.innerHTML = "";
1284
1285
	for (let i = 0; i < ae.GetContactCount(); i++) {
1286
		const el = document.createElement("option");
1287
		el.value = ae.GetContactMail(i);
1288
		lst.appendChild(el);
1289
	}
1290
1291
	if (ae.IsUserAdmin()) {
1292
		const el = document.createElement("option");
1293
		el.value = "public";
1294
		lst.appendChild(el);
1295
	}
1296
}
1297
1298
function addContact(mail, name, note) {
1299
	const tbl = document.getElementById("tbl_ctact");
1300
	const row = tbl.insertRow(-1);
1301
	const cellMail = row.insertCell(-1);
1302
	const cellName = row.insertCell(-1);
1303
	const cellNote = row.insertCell(-1);
1304
	const cellBtnD = row.insertCell(-1);
1305
1306
	cellMail.autocapitalize = "off";
1307
	cellMail.spellcheck = false;
1308
	cellMail.inputMode = "email";
1309
1310
	cellName.autocapitalize = "words";
1311
	cellName.spellcheck = false;
1312
1313
	cellNote.autocapitalize = "off";
1314
	cellNote.spellcheck = false;
1315
1316
	cellMail.textContent = mail;
1317
	cellName.textContent = name;
1318
	cellNote.textContent = note;
1319
	cellBtnD.innerHTML = "<button type=\"button\">X</button>";
1320
1321
	cellMail.contentEditable = true;
1322
	cellName.contentEditable = true;
1323
	cellNote.contentEditable = true;
1324
1325
	cellBtnD.onclick = function() {row.remove();};
1326
}
1327
1328
document.getElementById("btn_newcontact").onclick = function() {
1329
	addContact("", "", "");
1330
};
1331
1332
document.getElementById("btn_savecontacts").onclick = function() {
1333
	while (ae.GetContactCount() > 0) {
1334
		ae.DeleteContact(0);
1335
	}
1336
1337
	for (const row of document.getElementById("tbl_ctact").rows) {
1338
		ae.AddContact(row.cells[0].textContent, row.cells[1].textContent, row.cells[2].textContent);
1339
	}
1340
1341
	refreshContactList();
1342
1343
	const btn = this;
1344
	btn.disabled = true;
1345
1346
	ae.Private_Update(function(error) {
1347
		btn.disabled = false;
1348
		if (error) errorDialog(error);
1349
	});
1350
};
1351
1352
function writeVerify() {
1353
	if (
1354
	   !document.getElementById("write_recv").reportValidity()
1355
	|| !document.getElementById("write_subj").reportValidity()
1356
	|| !document.getElementById("write_body").reportValidity()
1357
	) {tabs[TAB_WRITE].cur = 0; return;}
1358
1359
	document.getElementById("div_write_1").hidden = true;
1360
	document.getElementById("div_write_2").hidden = false;
1361
1362
	document.getElementById("write2_recv").textContent = document.getElementById("write_recv").value;
1363
	document.getElementById("write2_subj").textContent = document.getElementById("write_subj").value;
1364
	document.getElementById("write2_rply").textContent = document.getElementById("write_subj").getAttribute("data-replyid");
1365
	document.getElementById("write2_body").textContent = document.getElementById("write_body").value;
1366
1367
	if (document.getElementById("write_recv").value.indexOf("@") >= 0) {
1368
		document.getElementById("write2_from").textContent = document.getElementById("write_from").value + "@" + ae.GetDomainEml();
1369
		document.getElementById("write2_pkey").hidden = true;
1370
	} else {
1371
		document.getElementById("write2_from").textContent = document.getElementById("write_from").value;
1372
		document.getElementById("write2_pkey").hidden = (document.getElementById("write_recv").value === "public");
1373
	}
1374
1375
	document.querySelector("#write2_send > button").disabled = false;
1376
	document.getElementById("write2_btntxt").textContent = (document.getElementById("write_recv").value === "public") ? "Make" : "Send to";
1377
}
1378
1379
function updateTab() {
1380
	switch (tab) {
1381
		case TAB_INBOX:
1382
			addMessages();
1383
		break;
1384
1385
		case TAB_DRBOX:
1386
			addSent();
1387
		break;
1388
1389
		case TAB_WRITE:
1390
			if (tabs[tab].cur === 0) {
1391
				document.getElementById("div_write_1").hidden = false;
1392
				document.getElementById("div_write_2").hidden = true;
1393
				document.getElementById("write_body").focus();
1394
			} else {
1395
				writeVerify();
1396
			}
1397
		break;
1398
1399
		case TAB_NOTES:
1400
			for (let i = 0; i <= tabs[tab].max; i++) {
1401
				document.getElementById("div_notes").children[i].hidden = (i !== tabs[tab].cur);
1402
			}
1403
		break;
1404
1405
		case TAB_TOOLS:
1406
			for (let i = 0; i <= tabs[tab].max; i++) {
1407
				document.getElementById("div_tools").children[i].hidden = (i !== tabs[tab].cur);
1408
			}
1409
		break;
1410
	}
1411
1412
	document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
1413
	document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1414
}
1415
1416
document.getElementById("btn_left").onclick = function() {
1417
	tabs[tab].cur--;
1418
	if (tabs[tab].cur === 0) this.disabled = true;
1419
	if (tabs[tab].cur < tabs[tab].max) document.getElementById("btn_rght").disabled = false;
1420
	updateTab();
1421
	this.blur();
1422
};
1423
1424
document.getElementById("btn_rght").onclick = function() {
1425
	tabs[tab].cur++;
1426
	if (tabs[tab].cur === tabs[tab].max) this.disabled = true;
1427
	document.getElementById("btn_left").disabled = false;
1428
	updateTab();
1429
	this.blur();
1430
};
1431
1432
const buttons = document.querySelectorAll("#main1 > .top > button");
1433
for (let i = 0; i < buttons.length; i++) {
1434
	buttons[i].onclick = function() {
1435
		tab = i;
1436
1437
		for (let j = 0; j < buttons.length; j++) {
1438
			document.querySelectorAll("#main1 > .mid > div")[j].hidden = (tab !== j);
1439
			buttons[j].disabled = (tab === j);
1440
		}
1441
1442
		document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
0 ignored issues
show
Bug introduced by
The variable tab is changed as part of the for loop for example by i on line 1435. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
1443
		document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1444
		document.getElementById("btn_dele").disabled = !tabs[tab].btnDele;
1445
		document.getElementById("btn_updt").disabled = !tabs[tab].btnUpdt;
1446
1447
		updateTab();
1448
	};
1449
}
1450
1451
function addressCreate(addr) {
1452
	document.getElementById("btn_address_create_normal").disabled = true;
1453
	document.getElementById("btn_address_create_shield").disabled = true;
1454
1455
	ae.Address_Create(addr, function(error1) {
1456
		if (error1 === 0) {
1457
			ae.Private_Update(function(error2) {
1458
				updateAddressCounts();
1459
1460
				if (error2 === 0) {
1461
					addAddress(ae.GetAddressCount() - 1);
1462
					if (addr !== "SHIELD") {
1463
						document.getElementById("txt_address_create_normal").value = "";
1464
						document.getElementById("txt_address_create_normal").focus();
1465
					}
1466
				} else errorDialog(error2);
1467
			});
1468
		} else {
1469
			errorDialog(error1);
1470
			updateAddressCounts();
1471
		}
1472
	});
1473
}
1474
1475
document.getElementById("btn_address_create_normal").onclick = function() {
1476
	if (ae.GetAddressCountNormal() >= ae.GetLimitNormalA(ae.GetUserLevel()) || ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31) return;
1477
1478
	const txtNewAddr = document.getElementById("txt_address_create_normal");
1479
	if (!txtNewAddr.reportValidity()) return;
1480
1481
	addressCreate(txtNewAddr.value);
1482
};
1483
1484
document.getElementById("btn_address_create_shield").onclick = function() {
1485
	if (ae.GetAddressCountShield() >= ae.GetLimitShieldA(ae.GetUserLevel()) || ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31) return;
1486
1487
	addressCreate("SHIELD");
1488
};
1489
1490
document.getElementById("btn_address_update").onclick = function() {
1491
	const btn = this;
1492
	btn.disabled = true;
1493
1494
	const rows = document.getElementById("tbl_addrs").rows;
1495
1496
	for (let i = 0; i < rows.length; i++) {
1497
		ae.SetAddressAccExt(i, rows[i].getElementsByTagName("input")[0].checked);
1498
		ae.SetAddressAccInt(i, rows[i].getElementsByTagName("input")[1].checked);
1499
	}
1500
1501
	ae.Address_Update(function(error) {
1502
		btn.disabled = false;
1503
		if (error) errorDialog(error);
1504
	});
1505
};
1506
1507
1508
document.getElementById("txt_reg").onkeyup = function(event) {
1509
	if (event.key === "Enter") {
1510
		event.preventDefault();
1511
		document.getElementById("btn_reg").click();
1512
	}
1513
};
1514
1515
document.getElementById("btn_reg").onclick = function() {
1516
	const btn = document.getElementById("btn_reg");
1517
	const txt = document.getElementById("txt_reg");
1518
	if (!txt.reportValidity()) return;
1519
	btn.disabled = true;
1520
1521
	ae.Account_Create(txt.value, function(error) {
1522
		if (error === 0) {
1523
			addAccountToTable(ae.Admin_GetUserCount() - 1);
1524
			txt.value = "";
1525
		} else errorDialog(error);
1526
1527
		btn.disabled = false;
1528
	});
1529
};
1530
1531
document.getElementById("chk_delme").onclick = function() {document.getElementById("btn_delme").disabled = !this.checked;};
1532
document.getElementById("chk_lowme").onclick = function() {document.getElementById("btn_lowme").disabled = !this.checked;};
1533
1534
document.getElementById("btn_notepad_saveupl").onclick = function() {
1535
	const np = document.getElementById("txt_notepad");
1536
	np.disabled = true;
1537
1538
	let fname = prompt("Save as...", "Untitled");
0 ignored issues
show
Debugging Code Best Practice introduced by
The prompt UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
1539
	if (!fname.endsWith(".txt")) fname += ".txt";
1540
1541
	ae.Message_Upload(fname, np.value, function(error) {
1542
		if (error === 0) {
1543
			np.value = "";
1544
			addUploads();
1545
			document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
1546
		} else errorDialog(error);
1547
1548
		np.disabled = false;
1549
	});
1550
};
1551
1552
document.getElementById("btn_upload").onclick = function() {
1553
	const btn = this;
1554
	const fileSelector = document.createElement("input");
1555
	fileSelector.type = "file";
1556
	fileSelector.click();
1557
1558
	fileSelector.onchange = function() {
1559
		btn.disabled = true;
1560
1561
		const reader = new FileReader();
1562
		reader.onload = function() {
1563
			ae.Message_Upload(fileSelector.files[0].name, new Uint8Array(reader.result), function(error) {
1564
				if (error === 0) {
1565
					addUploads();
1566
					document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
1567
				} else errorDialog(error);
1568
1569
				btn.disabled = false;
1570
			});
1571
		};
1572
1573
		reader.readAsArrayBuffer(fileSelector.files[0]);
1574
	};
1575
};
1576
1577
document.getElementById("btn_pg").onclick = function() {
1578
	localStorage.greeting = document.getElementById("txt_pg").value;
1579
};
1580
1581
document.querySelector("#write2_send > button").onclick = function() {
1582
	const btn = this;
1583
	btn.disabled = true;
1584
1585
	// Public announcement
1586
	if (document.getElementById("write2_recv").textContent === "public") {
1587
		ae.Message_Public(document.getElementById("write_subj").value, document.getElementById("write_body").value, function(error) {
1588
			if (error === 0) {
1589
				document.getElementById("write2_btntxt").textContent = "Announced to";
1590
				document.getElementById("write_recv").value = "";
1591
				document.getElementById("write_subj").value = "";
1592
				document.getElementById("write_body").value = "";
1593
			} else {
1594
				// TODO display error
1595
				document.getElementById("write2_btntxt").textContent = "Retry making";
1596
				btn.disabled = false;
1597
			}
1598
		});
1599
1600
		return;
1601
	}
1602
1603
	// Email or internal message
1604
	document.getElementById("write2_btntxt").textContent = "Sending to";
1605
1606
	ae.Message_Create(
1607
		document.getElementById("write_subj").value,
1608
		document.getElementById("write_body").value,
1609
		document.getElementById("write_from").value,
1610
		document.getElementById("write_recv").value,
1611
		document.getElementById("write_subj").getAttribute("data-replyid"),
1612
		(document.getElementById("write2_recv").textContent.indexOf("@") > 0) ? null : sodium.from_base64(document.querySelector("#write2_pkey > input").value, sodium.base64_variants.ORIGINAL_NO_PADDING),
1613
		function(error) {
1614
			if (error === 0) {
1615
				document.getElementById("write2_btntxt").textContent = "Delivered to";
1616
				document.getElementById("write_recv").value = "";
1617
				document.getElementById("write_subj").value = "";
1618
				document.getElementById("write_body").value = "";
1619
			} else {
1620
				errorDialog(error);
1621
1622
				document.getElementById("write2_btntxt").textContent = "Retry sending to";
1623
				btn.disabled = false;
1624
			}
1625
		}
1626
	);
1627
};
1628
1629
document.getElementById("txt_skey").onfocus = function() {
1630
	document.getElementById("greeting").textContent = localStorage.greeting;
1631
};
1632
1633
document.getElementById("txt_skey").onkeyup = function(event) {
1634
	if (event.key === "Enter") {
1635
		event.preventDefault();
1636
		document.getElementById("btn_enter").click();
1637
	}
1638
};
1639
1640
document.getElementById("btn_enter").onclick = function() {
1641
	const txtSkey = document.getElementById("txt_skey");
1642
1643
	if (txtSkey.value === "") {
1644
		ae.Reset();
1645
		document.getElementById("greeting").textContent = "Data cleared";
1646
		return;
1647
	}
1648
1649
	if (!txtSkey.reportValidity()) return;
1650
1651
	const btn = this;
1652
	btn.disabled = true;
1653
1654
	document.getElementById("txt_skey").disabled = true;
1655
	document.getElementById("txt_skey").style.background = "#233";
1656
1657
	ae.SetKeys(txtSkey.value, function(successSetKeys) {
1658
		if (successSetKeys) {
1659
			document.body.style.cursor = "wait";
1660
1661
			ae.Message_Browse(false, true, function(statusBrowse) {
1662
				document.body.style.cursor = "auto";
1663
1664
				if (statusBrowse === 0) {
1665
					txtSkey.value = "";
1666
					document.getElementById("div_begin").hidden = true;
1667
					document.getElementById("div_main").hidden = false;
1668
					reloadAccount();
1669
1670
					if (ae.IsUserAdmin()) {
1671
						ae.Account_Browse(function(statusAcc) {
1672
							if (statusAcc === 0) {
1673
								for (let i = 0; i < ae.Admin_GetUserCount(); i++) {addAccountToTable(i);}
1674
							} else {
1675
								errorDialog(statusAcc);
1676
							}
1677
						});
1678
					}
1679
				} else {
1680
					document.getElementById("txt_skey").disabled = false;
1681
					document.getElementById("txt_skey").style.background = "#466";
1682
					btn.focus();
1683
1684
					document.getElementById("greeting").textContent = getErrorMessage(statusBrowse) + " (0x" + statusBrowse.toString(16).padStart(2, "0").toUpperCase() + ")";
1685
					btn.disabled = false;
1686
				}
1687
			});
1688
		} else {
1689
			document.getElementById("txt_skey").disabled = false;
1690
			document.getElementById("txt_skey").style.background = "#466";
1691
			txtSkey.focus();
1692
1693
			document.getElementById("greeting").textContent = "SetKeys failed";
1694
			btn.disabled = false;
1695
		}
1696
	});
1697
};
1698
1699
});
1700